/*
 * $RCSfile: DebuggablePreparedStatement.java,v $
 * $Revision: 1.22 $
 *
 * Comments:
 *
 * (C) Copyright Biuro-Technika 2007.  All rights reserved.
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF Biuro-Technika
 * The copyright notice above does not evidence any
 * actual or intended publication of such source code.
 *
 * $Author: michal $          $Locker:  $
 * $Date: 2010/06/13 07:43:57 $
 * $Log: DebuggablePreparedStatement.java,v $
 * Revision 1.22  2010/06/13 07:43:57  michal
 * optymalizacja petli,poprawa odczyty wlasciwosci
 *
 * Revision 1.21  2010/04/20 14:15:30  michal
 * parametryzacja kodu, poprawa dostepnosci pol
 *
 * Revision 1.20  2010/02/10 08:48:47  michal
 * poprawa wykonywania zapytania przy query na wyjsciu
 *
 * Revision 1.19  2009/12/22 07:34:13  michal
 * poprawki do pelnego query na wyjsciu
 *
 * Revision 1.18  2009/11/02 12:04:56  michal
 * poprawki pod 1.4
 *
 * Revision 1.17  2009/11/02 11:14:50  michal
 * dodanie obslugi reszty typow
 *
 * Revision 1.16  2009/10/22 12:40:11  michal
 * za wolno tworzylo sie zapytanie
 *
 * Revision 1.15  2009/09/22 11:46:35  michal
 * ParaSoft -> Biuro-Technika
 *
 * Revision 1.14  2009/09/21 12:55:02  michal
 * poprawa logowania query
 *
 * Revision 1.13  2009/09/16 09:18:12  michal
 * poprawka przy wyswietlaniu bledow przy query
 *
 * Revision 1.12  2009/09/04 09:06:41  michal
 * wyrzucanie pelnego query do loga przy centrap.reports.query.debug=full
 *
 * Revision 1.11  2009/05/15 07:08:35  michal
 * wyswietlanie zapytan w przypadku bledu przy zapytaniach
 *
 * Revision 1.10  2009/04/20 10:41:45  michal
 * eliminacja tworzenia instancji obiektu Boolean
 *
 * Revision 1.9  2009/02/10 06:59:05  michal
 * dodanie obslugi pola typu Timestamp w zapytaniach
 *
 * Revision 1.8  2008/10/02 06:34:52  michal
 * poprawa ustalania powodu bledu SQL
 *
 * Revision 1.7  2007/12/06 10:26:43  rudi
 * Dodanie mozliwosci tworzenia zapytan typu scrollable
 *
 * Revision 1.6  2007/06/29 13:53:29  dominik
 * Zamiana ResultSet na NotNullResultSet
 *
 * Revision 1.5  2007/06/20 13:00:20  dominik
 * Skrypt dla tomcata 5.5
 *
 * Revision 1.4  2007/03/05 17:13:37  rudi
 * Dostosowanie do javy 6.0
 *
 * Revision 1.3  2007/02/06 14:04:23  dominik
 * Usuniecie nie wykorzystywanego pola user
 *
 * Revision 1.2  2007/02/05 13:23:54  dominik
 * Ustawienie zmiennej dbConn
 *
 * Revision 1.1  2007/01/17 08:53:51  dominik
 * Debugowy PreparedStatement
 *
 */

package parasoft.centrap.connection;

import static parasoft.centrap.utils.Error.null_connection;
import static parasoft.centrap.utils.Error.sql;
import static parasoft.centrap.utils.Error.sql_command;
import static parasoft.centrap.utils.Error.sql_command_missing_var;
import static parasoft.centrap.utils.Error.sql_command_param_length;
import static parasoft.centrap.utils.Error.unsupported_operation;

import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.Date;
import java.sql.NClob;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.StringTokenizer;

public class DebuggablePreparedStatement
    implements PreparedStatement
{
	protected PreparedStatement ps = null; //oryginalny preparedStatement
    protected String filteredSql = null;   //zapytanie bez pytajnikow oznaczajacych cos innego niz parametr
    protected DebugObject[] variables;     //tablica z parametrami
    private long startTime;              //czas rozpoczecia wykonania zapytania
    private long executeTime;            //czas zakonczenia wykonania zapytania
    private final boolean logged = true;
    protected DbConnection dbConn = null;
    protected String toString = null;//TODO is never used locally

    public DebuggablePreparedStatement(final DbConnection dbConn, final String sqlStatement) throws SQLException
    {
        this(dbConn.getConnection(), sqlStatement);
        this.dbConn = dbConn;
    }

    public DebuggablePreparedStatement(final Connection conn, final String sqlStatement, final DbConnection dbConn) throws SQLException
    {
        this(conn, sqlStatement);
        this.dbConn = dbConn;
    }

    public DebuggablePreparedStatement(final Connection conn, final String sqlStatement, final DbConnection dbConn, final boolean scrollable) throws SQLException{
    	this(conn, sqlStatement, scrollable);
    	this.dbConn = dbConn;
    }

    public DebuggablePreparedStatement(final Connection conn, final String sqlStatement) throws SQLException{
    	this(conn, sqlStatement, false);
    }

    public DebuggablePreparedStatement(final Connection conn, final String sqlStatement, final boolean scrollable) throws SQLException
    {
        if (conn == null) {
            throw null_connection.sql();
        }
        if (scrollable){
        	ps = conn.prepareStatement(sqlStatement, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
        } else {
        	ps = conn.prepareStatement(sqlStatement);
        }

        //sprawdza czy ktorykolwiek ze znakow '?' nie jest polem na parametr a jesli
        //nie jest to filtruje znak
        boolean isString = false;
        for (char sqlC : sqlStatement.toCharArray()) {
            if (sqlC == '\'') {
				isString = !isString;
			}
                //substitute the ? with an unprintable character if the ? is in a
                //string.
            if ((sqlC == '?') && isString) {
				sqlC = '\u0007';
			}
        }
        filteredSql = new String(sqlStatement);

        //find out how many variables are present in statement.
        int count = 0;
        int index = -1;
        while ( (index = filteredSql.indexOf("?", index + 1)) != -1) {
            count++;
        }

        //create array for bind variables
        variables = new DebugObject[count];

    }

    @Override
	public ResultSet executeQuery() throws SQLException
    {
        //execute query
        ResultSet results = null;
        try {
            results = new NotNullResultSet((ResultSet) executeVerboseQuery("executeQuery", null));
        }
        catch (final Exception e) {
            e.printStackTrace();
            String message = e.getMessage();
            if( (message == null) && (e.getCause()!=null)){
            	message = e.getCause().toString();
            }
            throw sql_command.sql(message, filteredSql);
        }
        return results;
    }

    @Override
	public int executeUpdate() throws SQLException
    {
        //execute update
        Integer results = null;
        try {
            results = (Integer) executeVerboseQuery("executeUpdate", null);
        }
        catch (final Exception e) {
            e.printStackTrace();
            String message = e.getMessage();
            if( message == null){
            	message = e.getCause().toString();
            }
            throw sql_command.sql(message, filteredSql);
        }
        return results.intValue();
    }

    @Override
	public void setNull(final int parameterIndex, final int sqlType) throws SQLException
    {
        ps.setNull(parameterIndex, sqlType);
    }

    @Override
	public void setBoolean(final int parameterIndex, final boolean x) throws SQLException
    {
        saveObject(parameterIndex, Boolean.valueOf(x));
        ps.setBoolean(parameterIndex, x);
    }

    @Override
	public void setByte(final int parameterIndex, final byte x) throws SQLException
    {
        saveObject(parameterIndex, new Byte(x));
        ps.setByte(parameterIndex, x);
    }

    @Override
	public void setShort(final int parameterIndex, final short x) throws SQLException
    {
        saveObject(parameterIndex, new Short(x));
        ps.setShort(parameterIndex, x);
    }

    @Override
	public void setInt(final int parameterIndex, final int x) throws SQLException
    {
        saveObject(parameterIndex, new Integer(x));
        ps.setInt(parameterIndex, x);
    }

    @Override
	public void setLong(final int parameterIndex, final long x) throws SQLException
    {
        saveObject(parameterIndex, new Long(x));
        ps.setLong(parameterIndex, x);
    }

    @Override
	public void setFloat(final int parameterIndex, final float x) throws SQLException
    {
        saveObject(parameterIndex, new Float(x));
        ps.setFloat(parameterIndex, x);
    }

    @Override
	public void setDouble(final int parameterIndex, final double x) throws SQLException
    {
        saveObject(parameterIndex, new Double(x));
        ps.setDouble(parameterIndex, x);
    }

    @Override
	public void setBigDecimal(final int parameterIndex, final BigDecimal x) throws SQLException
    {
        ps.setBigDecimal(parameterIndex, x);
    }

    @Override
	public void setString(final int parameterIndex, final String x) throws SQLException
    {
        saveObject(parameterIndex, x);
        ps.setString(parameterIndex, x);
    }

    @Override
	public void setBytes(final int parameterIndex, final byte[] x) throws SQLException
    {
    	saveObject(parameterIndex, x);
        ps.setBytes(parameterIndex, x);
    }

    @Override
	public void setDate(final int parameterIndex, final Date x) throws SQLException
    {
        saveObject(parameterIndex, x);
        ps.setDate(parameterIndex, x);
    }
    public void setDate(final int parameterIndex, final long x) throws SQLException
    {
        saveObject(parameterIndex, x);
        ps.setDate(parameterIndex, new java.sql.Date(x));
    }
    public void setDate(final int parameterIndex, final String x) throws SQLException
    {
        saveObject(parameterIndex, x);
        ps.setDate(parameterIndex, Date.valueOf(x));
    }

    @Override
	public void setTime(final int parameterIndex, final Time x) throws SQLException
    {
        saveObject(parameterIndex, x);
        ps.setTime(parameterIndex, x);
    }
    public void setTime(final int parameterIndex, final long x) throws SQLException
    {
        saveObject(parameterIndex, x);
        ps.setTime(parameterIndex, new Time(x));
    }
    public void setTime(final int parameterIndex, final String x) throws SQLException
    {
        saveObject(parameterIndex, x);
        ps.setTime(parameterIndex, Time.valueOf(x));
    }

    @Override
	public void setTimestamp(final int parameterIndex, final Timestamp x) throws SQLException
    {
        saveObject(parameterIndex, x);
        ps.setTimestamp(parameterIndex, x);
    }
    public void setTimestamp(final int parameterIndex, final long x) throws SQLException
    {
        saveObject(parameterIndex, x);
        ps.setTimestamp(parameterIndex, new Timestamp(x));
    }
    public void setTimestamp(final int parameterIndex, final String x) throws SQLException
    {
        saveObject(parameterIndex, x);
        ps.setTimestamp(parameterIndex, Timestamp.valueOf(x));
    }

    @Override
	public void setAsciiStream(final int parameterIndex, final InputStream x, final int length) throws SQLException
    {
        ps.setAsciiStream(parameterIndex, x, length);
    }

    @Override
	public void setUnicodeStream(final int parameterIndex, final InputStream x, final int length) throws SQLException
    {
        throw unsupported_operation.unsupportedOperation("setUnicodeStream");
    }

    @Override
	public void setBinaryStream(final int parameterIndex, final InputStream x, final int length) throws SQLException
    {
        ps.setBinaryStream(parameterIndex, x, length);
    }

    @Override
	public void clearParameters() throws SQLException
    {
        ps.clearParameters();
    }

    @Override
	public void setObject(final int parameterIndex, final Object x, final int targetSqlType, final int scale) throws
        SQLException
    {
        ps.setObject(parameterIndex, x, targetSqlType, scale);
    }

    @Override
	public void setObject(final int parameterIndex, final Object x, final int targetSqlType) throws SQLException
    {
        ps.setObject(parameterIndex, x, targetSqlType);
    }

    @Override
	public void setObject(final int parameterIndex, final Object x) throws SQLException
    {
        saveObject(parameterIndex, x);
        ps.setObject(parameterIndex, x);
    }

    @Override
	public boolean execute() throws SQLException
    {
        //execute query
        Boolean results = null;
        try {
            results = (Boolean) executeVerboseQuery("execute", null);
        }
        catch (final Exception e) {
            e.printStackTrace();
            String message = e.getMessage();
            if( message == null){
            	message = e.getCause().toString();
            }
            throw sql_command.sql(message, filteredSql);
        }
        return results.booleanValue();
    }

    @Override
	public void addBatch() throws SQLException
    {
        ps.addBatch();
    }

    @Override
	public void setCharacterStream(final int parameterIndex, final Reader reader, final int length) throws
        SQLException
    {
        ps.setCharacterStream(parameterIndex, reader, length);
    }

    @Override
	public void setRef(final int i, final Ref x) throws SQLException
    {
        ps.setRef(i, x);
    }

    @Override
	public void setBlob(final int i, final Blob x) throws SQLException
    {
        ps.setBlob(i, x);
    }

    @Override
	public void setClob(final int i, final Clob x) throws SQLException
    {
        ps.setClob(i, x);
    }

    @Override
	public void setArray(final int i, final Array x) throws SQLException
    {
        ps.setArray(i, x);
    }

    @Override
	public ResultSetMetaData getMetaData() throws SQLException
    {
        return ps.getMetaData();
    }

    @Override
	public void setDate(final int parameterIndex, final Date x, final Calendar cal) throws SQLException
    {
        ps.setDate(parameterIndex, x, cal);
    }

    @Override
	public void setTime(final int parameterIndex, final Time x, final Calendar cal) throws SQLException
    {
        ps.setTime(parameterIndex, x, cal);
    }

    @Override
	public void setTimestamp(final int parameterIndex, final Timestamp x, final Calendar cal) throws SQLException
    {
        ps.setTimestamp(parameterIndex, x, cal);
    }

    @Override
	public void setNull(final int paramIndex, final int sqlType, final String typeName) throws SQLException
    {
        ps.setNull(paramIndex, sqlType, typeName);
    }

    @Override
	public void setURL(final int parameterIndex, final URL x) throws SQLException
    {
        ps.setURL(parameterIndex, x);
    }

    @Override
	public ParameterMetaData getParameterMetaData() throws SQLException
    {
        return ps.getParameterMetaData();
    }

    @Override
	public ResultSet executeQuery(final String sql) throws SQLException
    {
        //execute query
        ResultSet results = null;
        try {
            results = new NotNullResultSet((ResultSet) executeVerboseQuery("executeQuery", new Class[] {sql.getClass()}));
        }
        catch (final Exception e) {
            e.printStackTrace();
            String message = e.getMessage();
            if( message == null){
            	message = e.getCause().toString();
            }
            throw sql_command.sql(message, filteredSql);
        }
        return results;
    }

    @Override
	public int executeUpdate(final String sql) throws SQLException
    {
        //execute query
        Integer results = null;
        try {
            results = (Integer) executeVerboseQuery("executeUpdate", new Class[] {sql.getClass()});
        }
        catch (final Exception e) {
            e.printStackTrace();
            String message = e.getMessage();
            if( message == null){
            	message = e.getCause().toString();
            }
            throw sql_command.sql(message, filteredSql);
        }
        return results.intValue();
    }

    @Override
	public void close() throws SQLException
    {
        ps.close();
    }

    @Override
	public int getMaxFieldSize() throws SQLException
    {
        return ps.getMaxFieldSize();
    }

    @Override
	public void setMaxFieldSize(final int max) throws SQLException
    {
        ps.setMaxFieldSize(max);
    }

    @Override
	public int getMaxRows() throws SQLException
    {
        return ps.getMaxRows();
    }

    @Override
	public void setMaxRows(final int max) throws SQLException
    {
        ps.setMaxRows(max);
    }

    @Override
	public void setEscapeProcessing(final boolean enable) throws SQLException
    {
        ps.setEscapeProcessing(enable);
    }

    @Override
	public int getQueryTimeout() throws SQLException
    {
        return ps.getQueryTimeout();
    }

    @Override
	public void setQueryTimeout(final int seconds) throws SQLException
    {
        ps.setQueryTimeout(seconds);
    }

    @Override
	public void cancel() throws SQLException
    {
        ps.cancel();
    }

    @Override
	public SQLWarning getWarnings() throws SQLException
    {
        return ps.getWarnings();
    }

    @Override
	public void clearWarnings() throws SQLException
    {
        ps.clearWarnings();
    }

    @Override
	public void setCursorName(final String name) throws SQLException
    {
        ps.setCursorName(name);
    }

    @Override
	public boolean execute(final String sql) throws SQLException
    {
        //execute query
        Boolean results = null;
        try {
            results = (Boolean) executeVerboseQuery("execute", new Class[] {sql.getClass()});
        }
        catch (final Exception e) {
            e.printStackTrace();
            String message = e.getMessage();
            if( message == null){
            	message = e.getCause().toString();
            }
            throw sql_command.sql(message, filteredSql);
        }
        return results.booleanValue();
    }

    @Override
	public ResultSet getResultSet() throws SQLException
    {
        return ps.getResultSet();
    }

    @Override
	public int getUpdateCount() throws SQLException
    {
        return ps.getUpdateCount();
    }

    @Override
	public boolean getMoreResults() throws SQLException
    {
        return ps.getMoreResults();
    }

    @Override
	public void setFetchDirection(final int direction) throws SQLException
    {
        ps.setFetchDirection(direction);
    }

    @Override
	public int getFetchDirection() throws SQLException
    {
        return ps.getFetchDirection();
    }

    @Override
	public void setFetchSize(final int rows) throws SQLException
    {
        ps.setFetchSize(rows);
    }

    @Override
	public int getFetchSize() throws SQLException
    {
        return ps.getFetchSize();
    }

    @Override
	public int getResultSetConcurrency() throws SQLException
    {
        return ps.getResultSetConcurrency();
    }

    @Override
	public int getResultSetType() throws SQLException
    {
        return ps.getResultSetType();
    }

    @Override
	public void addBatch(final String sql) throws SQLException
    {
        ps.addBatch(sql);
    }

    @Override
	public void clearBatch() throws SQLException
    {
        ps.clearBatch();
    }

    @Override
	public int[] executeBatch() throws SQLException
    {
        return ps.executeBatch();
    }

    @Override
	public Connection getConnection() throws SQLException
    {
        return ps.getConnection();
    }

    @Override
	public boolean getMoreResults(final int current) throws SQLException
    {
        return ps.getMoreResults(current);
    }

    @Override
	public ResultSet getGeneratedKeys() throws SQLException
    {
        return ps.getGeneratedKeys();
    }

    @Override
	public int executeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException
    {
        return ps.executeUpdate(sql, autoGeneratedKeys);
    }

    @Override
	public int executeUpdate(final String sql, final int[] columnIndexes) throws SQLException
    {
        return ps.executeUpdate(sql, columnIndexes);
    }

    @Override
	public int executeUpdate(final String sql, final String[] columnNames) throws SQLException
    {
        return ps.executeUpdate(sql, columnNames);
    }

    @Override
	public boolean execute(final String sql, final int autoGeneratedKeys) throws SQLException
    {
        return ps.execute(sql, autoGeneratedKeys);
    }

    @Override
	public boolean execute(final String sql, final int[] columnIndexes) throws SQLException
    {
        return ps.execute(sql, columnIndexes);
    }

    @Override
	public boolean execute(final String sql, final String[] columnNames) throws SQLException
    {
        return ps.execute(sql, columnNames);
    }

    @Override
	public int getResultSetHoldability() throws SQLException
    {
        return ps.getResultSetHoldability();
    }

    /**
     * sprawdza czy ilosc parametrow jest rowna ilosci pytajnikow
     * Sequence begins at 1.
     * @param parameterIndex which PreparedStatement parameter is being set.
     * @param o Object being stored as parameter
     * @throws IndexOutOfBoundsException if index exceeds number of variables.
     */
    private void saveObject(final int parameterIndex, final Object o) throws IndexOutOfBoundsException
    {
        if (parameterIndex > variables.length) {
            throw sql_command_param_length.index(String.valueOf(parameterIndex), String.valueOf(variables.length));
        }
        variables[parameterIndex - 1] = new DebugObject(o);
    }

    /**
     * this toString is overidden to return a String representation of
     * the sql statement being sent to the database. If a bind variable
     * is missing then the String contains a ? + (missing variable #)
     * @return the above string representation
     */
    @Override
	public String toString()
    {
        final StringTokenizer st = new StringTokenizer(filteredSql, "?");
        int count = 1;
        final StringBuffer statement = new StringBuffer();
        while (st.hasMoreTokens()) {
            statement.append(st.nextToken());
            if (count <= variables.length) {
                if ((variables[count - 1] != null) && variables[count - 1].isValueAssigned()) {
                    try {
                        statement.append(format(variables[count - 1].getDebugObject()));
                    }
                    catch (final SQLException e) {
                        statement.append(sql.toString());
                    }
                }
                else {
                    statement.append(sql_command_missing_var.format(String.valueOf(count)));
                }
            }
            count++;
        }
        //unfilter the string in case there where rogue '?' in query string.
        final char[] unfilterSql = statement.toString().toCharArray();
        for (int i = 0; i < unfilterSql.length; i++) {
            if (unfilterSql[i] == '\u0007') {
				unfilterSql[i] = '?';
			}
        }

        return (toString = new String(unfilterSql));
    }

    private Object executeVerboseQuery(final String methodName, final Class<?>[] parameters) throws SQLException,
    NoSuchMethodException, InvocationTargetException, IllegalAccessException
{
    //determine which method we have
    final Method m = ps.getClass().getMethod(methodName, parameters);

    //calculate execution time for verbose debugging
    start();
    final Object returnObject = m.invoke(ps, (Object[]) parameters);
    end();
    if (logged) {
        dbConn.makeSerwisLog(this.toString(), "DEBUG_PREPARE_STATEMENT", new Double(executeTime));
		}
    //return the executions return type
    return returnObject;
}

    private void start()
    {
        startTime = System.currentTimeMillis();
    }

    public long getStartTime(){
    	return startTime;
    }

    private void end()
    {
        executeTime = System.currentTimeMillis() - startTime;
    }

    public long getExecuteTime(){
    	return executeTime;
    }

    /**
     * If object is null, Blob, Clob, Array, Ref, or String this returns the value from the protected methods
     * in this class that take those Classes.
     * @param o Object to be formatted
     * @return formatted String
     * @throws SQLException
     */
    private String format(final Object o) throws SQLException
    {
        if (o == null) {
            return "NULL";
        }
        if (o instanceof String) {
        	return format( (String) o);
        }
        if (o instanceof java.sql.Date) {
            return format( (java.sql.Date) o);
        }
        if (o instanceof java.sql.Timestamp) {
            return format( (java.sql.Timestamp) o);
        }
        if (o instanceof Clob) {
        	return format( (Clob) o);
        }
        if (o instanceof Blob) {
        	return format( (Blob) o);
        }
        if (o instanceof Array) {
        	return format( (Array) o);
        }
        if (o instanceof Ref) {
        	return format( (Ref) o);
        }
        return o.toString();
    }

    /**
     * Formats Date object
     * @param date Date to be formatted
     * @return String
     * @exception SQLException
     */
    private String format(final java.sql.Date date)
    {
        return "'" + date.toString() + "'";
    }

    /**
     * Formats Timestamp object
     * @param date Timestamp to be formatted
     * @return String
     * @exception SQLException
     */
    private String format(final java.sql.Timestamp timestamp)
    {
        return "'" + timestamp.toString() + "'";
    }

    /**
     * Formats a blob to the following String "'<Blob length = " + blob.length()+">'"
     * This method's output will not translate directly into the database. It is informational only.
     * @param blob The blob to be translated
     * @return The String representation of the blob
     * @exception SQLException
     */
    private String format(final java.sql.Blob blob) throws SQLException
    {
        return "'<Blob length = " + blob.length() + ">'";
    }

    /**
     * Formats a clob to the following String "'<Clob length = " + clob.length()+">'"
     * This method's output will not translate directly into the database. It is informational only.
     * @param clob The clob to be translated
     * @return The String representation of the clob
     * @exception SQLException
     */
    private String format(final java.sql.Clob clob) throws SQLException
    {
        return "'<Clob length = " + clob.length() + ">'";
    }

    /**
     * Formats an Array to the following String "array.getBaseTypeName()"
     * This method's output will not translate directly into the database. It is informational only.
     * @param array The array to be translated
     * @return The base name of the array
     * @exception SQLException
     *
     */
    private String format(final java.sql.Array array) throws SQLException
    {
        return array.getBaseTypeName();
    }

    /**
     * Formats a Ref to the following String "ref.getBaseTypeName()"
     * This method's output will not translate directly into the database. It is informational only.
     * @param ref The ref to be translated
     * @return The base name of the ref
     * @exception SQLException
     */
    private String format(final java.sql.Ref ref) throws SQLException
    {
        return ref.getBaseTypeName();
    }

    /**
     * Checks the String for null and returns "'" + string + "'".
     * @param string String to be formatted
     * @return formatted String (null returns "NULL")
     * @throws SQLException
     */
    private String format(final java.lang.String string)
    {
        if (string.equals("NULL")){
            return string;
        }
        return new StringBuffer("'").append(string).append("'").toString();
    }

    private class DebugObject
    {
        private final Object debugObject;
        private final boolean valueAssigned;

        public DebugObject(final Object debugObject)
        {
            this.debugObject = debugObject;
            valueAssigned = true;
        }

        public Object getDebugObject()
        {
            return debugObject;
        }

        public boolean isValueAssigned()
        {
            return valueAssigned;
        }
    }

	@Override
	public void setAsciiStream(final int parameterIndex, final InputStream x) throws SQLException {
		// TODO Auto-generated method stub
	}

	@Override
	public void setAsciiStream(final int parameterIndex, final InputStream x, final long length) throws SQLException {
		saveObject(parameterIndex, x);
		ps.setAsciiStream(parameterIndex, x, (int) length);
	}

	@Override
	public void setBinaryStream(final int parameterIndex, final InputStream x) throws SQLException {
		// TODO Auto-generated method stub
	}

	@Override
	public void setBinaryStream(final int parameterIndex, final InputStream x, final long length) throws SQLException {
		saveObject(parameterIndex, x);
		ps.setBinaryStream(parameterIndex, x, (int) length);
	}

	@Override
	public void setBlob(final int parameterIndex, final InputStream inputStream) throws SQLException {
		saveObject(parameterIndex, inputStream);
		ps.setBlob(parameterIndex, (Blob) inputStream);
	}

	@Override
	public void setBlob(final int parameterIndex, final InputStream inputStream, final long length) throws SQLException {
		// TODO Auto-generated method stub
	}

	@Override
	public void setCharacterStream(final int parameterIndex, final Reader reader) throws SQLException {
		// TODO Auto-generated method stub
	}

	@Override
	public void setCharacterStream(final int parameterIndex, final Reader reader, final long length) throws SQLException {
		ps.setCharacterStream(parameterIndex, reader, (int) length);
	}

	@Override
	public void setClob(final int parameterIndex, final Reader reader) throws SQLException {
		ps.setClob(parameterIndex, (Clob) reader);
	}

	@Override
	public void setClob(final int parameterIndex, final Reader reader, final long length) throws SQLException {
		// TODO Auto-generated method stub
	}

	@Override
	public void setNCharacterStream(final int parameterIndex, final Reader value) throws SQLException {
		((DebuggablePreparedStatement) ps).setNCharacterStream(parameterIndex, value);
	}

	@Override
	public void setNCharacterStream(final int parameterIndex, final Reader value, final long length) throws SQLException {
		((DebuggablePreparedStatement) ps).setNCharacterStream(parameterIndex, value, length);
	}

	@Override
	public void setNClob(final int parameterIndex, final NClob value) throws SQLException {
		saveObject(parameterIndex, value);
		((DebuggablePreparedStatement) ps).setNClob(parameterIndex, value);
	}

	@Override
	public void setNClob(final int parameterIndex, final Reader reader) throws SQLException {
		((DebuggablePreparedStatement) ps).setNClob(parameterIndex, reader);
	}

	@Override
	public void setNClob(final int parameterIndex, final Reader reader, final long length) throws SQLException {
		((DebuggablePreparedStatement) ps).setNClob(parameterIndex, reader, length);
	}

	@Override
	public void setNString(final int parameterIndex, final String value) throws SQLException {
		saveObject(parameterIndex, value);
		((DebuggablePreparedStatement) ps).setNString(parameterIndex, value);
	}

	@Override
	public void setRowId(final int parameterIndex, final RowId x) throws SQLException {
		saveObject(parameterIndex, x);
		((DebuggablePreparedStatement) ps).setRowId(parameterIndex, x);
	}

	@Override
	public void setSQLXML(final int parameterIndex, final SQLXML xmlObject) throws SQLException {
		saveObject(parameterIndex, xmlObject);
		((DebuggablePreparedStatement) ps).setSQLXML(parameterIndex, xmlObject);
	}

	@Override
	public boolean isClosed() throws SQLException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isPoolable() throws SQLException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void setPoolable(final boolean poolable) throws SQLException {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean isWrapperFor(final Class<?> iface) throws SQLException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public <E>E unwrap(final Class<E> iface) throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

}
